using System;
using MSNPSharp;
using MSNPSharp.Core;
using MSNPSharp.DataTransfer;
using System.IO;


namespace MSNConnector {    
	public class Connection {

		public delegate void LoginEventHandler(object sender, System.EventArgs e);
		public event LoginEventHandler OnLogin;

		public long ConnectionRetryTimeout=60000;
		private MSNPSharp.Messenger messenger = new Messenger();

        System.Timers.Timer connTimer;

		public Connection() {
			connTimer = new System.Timers.Timer(60000);              
		}

		public string Email;
		public string Password;
        public void Login(string Email, string Password)
        {
            // set the events that we will handle
            // remember that the nameserver is the server that sends contact lists, notifies you of contact status changes, etc.
            // a switchboard server handles the individual conversation sessions.
            messenger.NameserverProcessor.ConnectionEstablished += new EventHandler<EventArgs>(NameserverProcessor_ConnectionEstablished);
            messenger.Nameserver.SignedIn += new EventHandler<EventArgs>(Nameserver_SignedIn);
            messenger.Nameserver.SignedOff += new EventHandler<SignedOffEventArgs>(Nameserver_SignedOff);
            messenger.NameserverProcessor.ConnectingException += new EventHandler<ExceptionEventArgs>(NameserverProcessor_ConnectingException);
            messenger.Nameserver.ExceptionOccurred += new EventHandler<ExceptionEventArgs>(Nameserver_ExceptionOccurred);
            messenger.Nameserver.AuthenticationError += new EventHandler<ExceptionEventArgs>(Nameserver_AuthenticationError);
            messenger.Nameserver.ServerErrorReceived += new EventHandler<MSNErrorEventArgs>(Nameserver_ServerErrorReceived);
            messenger.ConversationCreated += new EventHandler<ConversationCreatedEventArgs>(messenger_ConversationCreated);
            messenger.TransferInvitationReceived += new EventHandler<MSNSLPInvitationEventArgs>(messenger_TransferInvitationReceived);
            //messenger.Nameserver.ContactService.ReverseAdded += new EventHandler<ContactEventArgs>(ContactService_ReverseAdded);
            messenger.Nameserver.ContactService.SynchronizationCompleted += new EventHandler<EventArgs>(ContactService_SynchronizationCompleted);

            // everything is setup, now connect to the messenger service
            messenger.Credentials.Account = Email;
            messenger.Credentials.Password = Password;

            //messenger.Nameserver.SignedOff+=new SignedOffEventHandler(Nameserver_SignedOff);
            messenger.Connect();

        }

        void SynchReverseContacts()
        {
            foreach (MSNPSharp.Contact contact in messenger.ContactList.Values)
            {
                if (!IsAdded(contact.Mail))
                {
                    this.messenger.Nameserver.ContactService.AddNewContact(contact.Mail);
                    System.Diagnostics.Trace.WriteLine("New Contact Added:" + contact.Mail);
                }
            }
        }

        void ContactService_SynchronizationCompleted(object sender, EventArgs e)
        {
            SynchReverseContacts();
            Plugins.PluginLoader.RootStore = ServiceResourceFolder;
            Plugins.PluginLoader.Messenger = messenger;

            OnLogin(this, new System.EventArgs());
        }

        void ContactService_ReverseAdded(object sender, ContactEventArgs e)
        {
            throw new NotImplementedException();
        }
		public void StartMSN(string Email, string Password) {
			this.Email=Email;
			this.Password=Password;

            try {				
				connTimer.AutoReset=true;
				connTimer.Elapsed+=new System.Timers.ElapsedEventHandler(connTimer_Elapsed);

				SetStatus("Login");
				Login(Email, Password);
					
				SetStatus("Connection Timer Started");
				connTimer.Start();
				connTimer.Enabled=true;								
			}
			catch(Exception e) {
				// in case of an error, report this to the user (or developer)
				SetStatus("Connecting failed: " + e.ToString());
			}			

		}



		public void SetStatus(string status) {
			System.Diagnostics.Trace.WriteLine(status);
		}
		private void NameserverProcessor_ConnectionEstablished(object sender, EventArgs e) {
            SetStatus("Nameserver Processor Connected");
		}

		private void Nameserver_SignedIn(object sender, EventArgs e) {
			SetStatus("Signed into the messenger network as " + messenger.Owner.Name );
			// set our presence status
            messenger.Owner.StatusChanged += new EventHandler<StatusChangedEventArgs>(Owner_StatusChanged);
            messenger.Owner.Status = PresenceStatus.Online;
			//UpdateContactlist();
		}

        void Owner_StatusChanged(object sender, StatusChangedEventArgs e)
        {
            SetStatus("Status Changed:" + messenger.Owner.Status);
            if (messenger.Owner.Status == PresenceStatus.Online)
            {
                SetScreenName(Plugins.PluginLoader.ScreenName);
            }
        }

		public int ConnectionRetrySleep=10000;
		private void Nameserver_SignedOff(object sender, SignedOffEventArgs e) {
			if(ConnectionRetrySleep>0) {
				System.Threading.Thread.Sleep(ConnectionRetrySleep);
				messenger.Connect();		
			}
		}

		private void Nameserver_ExceptionOccurred(object sender, ExceptionEventArgs e) {
			// ignore the unauthorized exception, since we're handling that error in another method.
			if(e.Exception is UnauthorizedException)
				return;

			SetStatus("Nameserver exception:" + e.Exception.ToString());
		}

		private void NameserverProcessor_ConnectingException(object sender, ExceptionEventArgs e) {
			//MessageBox.Show(e.Exception.ToString(), "Connecting exception");
			SetStatus("Connecting failed:"+e.Exception.ToString());
			ConnectionRetrySleep=ConnectionRetrySleep*10;
			if(ConnectionRetrySleep>1000000) 
				ConnectionRetrySleep=0;
			else 
				this.messenger.Connect();
		}

		private void Nameserver_AuthenticationError(object sender, ExceptionEventArgs e) {
			//MessageBox.Show("Authentication failed, check your account or password.", "Authentication failed");
			ConnectionRetrySleep=0;
			SetStatus("Authentication failed:"+e.Exception.ToString());
		}



		/// <summary>
		/// Notifies the user of errors which are send by the MSN server.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void Nameserver_ServerErrorReceived(object sender, MSNErrorEventArgs e) {
			// when the MSN server sends an error code we want to be notified.
			SetStatus("Server error received:"+e.MSNError.ToString());
		}

		/// <summary>
		/// Displays the retrieved image in the image box on the form.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void session_TransferFinished(object sender, EventArgs e) {
			P2PTransferSession session = (P2PTransferSession)sender;
			DisplayImage image = (DisplayImage)session.ClientData;			
			image.RetrieveImage();

			//if(image.Image != null)
				//displayImageBox.Image = image.Image;			
		}





		private void messenger_ConversationCreated(object sender, ConversationCreatedEventArgs e) {
			// check if the request is initiated remote or by this object
			// if it is initiated remote then we have to create a conversation form. Otherwise the 
			// form is already created and we don't need to create another one.
            if (cEmail != null)
            {
                e.Conversation.Invite(cEmail, ClientType.PassportMember);
            }
            e.Conversation.Switchboard.ContactJoined+=new EventHandler<ContactEventArgs>(Switchboard_ContactJoined);
            e.Conversation.Switchboard.TextMessageReceived+=new EventHandler<TextMessageEventArgs>(Switchboard_TextMessageReceived);
		}

		
		public bool IsAdded(string Email) {
			foreach(MSNPSharp.Contact person in messenger.ContactList.Allowed) {
				if(person.Mail==Email) return true;
			}
			return false;
		}
        static string serviceResourceFolder = null;
        public static string ServiceResourceFolder
        {
            get
            {
                if (serviceResourceFolder == null)
                    serviceResourceFolder = System.Configuration.ConfigurationSettings.AppSettings["ServiceResourceFolder"];
                if (serviceResourceFolder == null || serviceResourceFolder.Trim() == "")
                {
                    serviceResourceFolder = @"c:\MSNService\";
                }
                if (!System.IO.Directory.Exists(serviceResourceFolder)) System.IO.Directory.CreateDirectory(serviceResourceFolder);
                return serviceResourceFolder;
            }
        }
		public void SaveContactList() {

			int count=1;
			System.Text.StringBuilder sb = new System.Text.StringBuilder();

			MSNPSharp.ContactList.ListEnumerator list = messenger.ContactList.All;

			string path = System.IO.Path.Combine(ServiceResourceFolder, messenger.Owner.Mail + @"\ContactLists\");
			if(!System.IO.Directory.Exists(path))System.IO.Directory.CreateDirectory(path);
			path = path + this.messenger.Owner.Mail + ".xml";
			

			sb.Append("<?xml version=\"1.0\"?><Contacts Owner=\""+this.messenger.Owner.Mail+"\">");
			foreach(MSNPSharp.Contact contact in list) {
				sb.Append(string.Format("<Contact ID=\""+count+"\">"));
				sb.Append(string.Format("<Blocked>{0}</Blocked>",contact.Blocked));
				//sb.Append(string.Format("<ContactGroupName>{0}</ContactGroupName>",contact.ContactGroup.Name));
				//sb.Append(string.Format("<FileLocation>{0}</FileLocation>",contact.DisplayImage.FileLocation));
				sb.Append(string.Format("<HomePhone>{0}</HomePhone>",contact.HomePhone));
				sb.Append(string.Format("<Mail>{0}</Mail>",contact.Mail));
				sb.Append(string.Format("<MobileAccess>{0}</MobileAccess>",contact.MobileAccess));
				sb.Append(string.Format("<MobileDevice>{0}</MobileDevice>",contact.MobileDevice));
				sb.Append(string.Format("<MobilePhone>{0}</MobilePhone>",contact.MobilePhone));
				sb.Append(string.Format("<Name>{0}</Name>",contact.Name));
				sb.Append(string.Format("<OnAllowedList>{0}</OnAllowedList>",contact.OnAllowedList));
				sb.Append(string.Format("<OnBlockedList>{0}</OnBlockedList>",contact.OnBlockedList));
				sb.Append(string.Format("<OnForwardList>{0}</OnForwardList>",contact.OnForwardList));
				sb.Append(string.Format("<Online>{0}</Online>",contact.Online));
				sb.Append(string.Format("<OnReverseList>{0}</OnReverseList>",contact.OnReverseList));
				sb.Append(string.Format("<Status>{0}</Status>",contact.Status));
				sb.Append(string.Format("<WorkPhone>{0}</WorkPhone>",contact.WorkPhone));
				sb.Append(string.Format("</Contact>"));
				count++;
			}
			sb.Append("</Contacts>");

			System.IO.StreamWriter sw = new StreamWriter(path);
			sw.Write(sb.ToString());
			sw.Close();
		}

		public string GetPartialScreenName() {
			return messenger.Owner.Name.Replace(" (AI Online)","").Replace(" (AI Offline)","");			
		}
        string cEmail = null;
        string cMsg = null;
        public void Chat(string Email, string msg)
        {
            cEmail = Email;
            cMsg = msg;
            messenger.CreateConversation();
            
        }
        public string GetStatus()
        {
            return messenger.Owner.Status.ToString();
        }
		public string GetFullScreenName() {
			return messenger.Owner.Name;
		}
		public void SetScreenName(string NewName) {
            //string add=" (AI Online)";
            //if(!this.AIOnline) add=" (AI Offline)";
            //messenger.Nameserver.SetScreenName(NewName+add);
            //
		}


		public System.Collections.Hashtable RandomResponses = new System.Collections.Hashtable();
		System.Random rnd = new Random();

		System.Collections.Hashtable pending = new System.Collections.Hashtable();


		public bool ContactsSaved=false;
		private void connTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
            System.Diagnostics.Trace.Flush();
			if(!ContactsSaved) SaveContactList();
			ContactsSaved=true;
			if(!messenger.Connected) {
				connTimer.Stop();
				try {
                    System.Diagnostics.Trace.WriteLine("MESSENGER is not CONNECTED, attempting reconnect");
                    this.messenger = new Messenger();
					StartMSN(Email, Password);
                    System.Diagnostics.Trace.WriteLine("StartMSN Called");
				}catch(Exception exception) {
                    System.Diagnostics.Trace.WriteLine(exception);
                }
				connTimer.Start();
			}
            SynchReverseContacts();
		}

		private void messenger_TransferInvitationReceived(object sender, MSNSLPInvitationEventArgs e) {
			string path = System.IO.Path.Combine(ServiceResourceFolder, messenger.Owner.Mail + "\\DownloadedFiles\\" + messenger.ContactList[e.TransferProperties.RemoteContact.Name].Mail + @"\");
			System.IO.Directory.CreateDirectory(path);
			path = path + e.Filename;
			e.TransferSession.DataStream = new System.IO.FileStream(path, FileMode.Create, FileAccess.Write);
			e.Accept = true;
			e.TransferSession.AutoCloseStream=true;

			LogConversation(messenger.ContactList[e.TransferProperties.RemoteContact.Name].Mail, "File Transfer Initiated:" + path, true);
			
			
			//sndr.SendTextMessage(new MSNPSharp.TextMessage(response.Replace("Anika", this.AIName)));
			/*
			e.Filename;
			e.FileSize;
			e.TransferProperties.RemoteContact;
			*/
			/*
			e.TransferSession.TransferAborted+=new EventHandler(TransferSession_TransferAborted);
			e.TransferSession.TransferFinished+=new EventHandler(TransferSession_TransferFinished);
			e.TransferSession.TransferStarted+=new EventHandler(TransferSession_TransferStarted);
			*/
		}

		private void Switchboard_ContactJoined(object sender, ContactEventArgs e) {
            if (e.Contact.Mail == cEmail && cMsg != null)
            {
                MSNPSharp.SBMessageHandler handler = (sender as MSNPSharp.SBMessageHandler);
                if (handler != null)
                {
                    handler.SendTextMessage(new TextMessage(cMsg));
                }
                cMsg = null;
                cEmail = null;
            }
		}

		public string DisplayImagePath="";
		private void LogConversation(string From, string Text, bool Response) {
			string convPath = System.IO.Path.Combine(ServiceResourceFolder, this.messenger.Owner.Mail + @"\Conversations\" + From + "\\");
			if(!System.IO.Directory.Exists(convPath)) System.IO.Directory.CreateDirectory(convPath);
			System.DateTime now = System.DateTime.Now;
			convPath = convPath + now.Year + "-" + now.Month + "-" + now.Day + ".log";			
			System.IO.StreamWriter sw = new StreamWriter(convPath,true);
			sw.Write("[" + now.ToString() + "] ");
			if(Response) {
				sw.Write(this.messenger.Owner.Mail + ":" + Text+"\r\n");
			} else {
				sw.Write(From + ":" + Text+"\r\n");
			}
			sw.Close();
		}
		Toolset.ConversationBlogger blogger = new Toolset.ConversationBlogger ();
        private void Switchboard_TextMessageReceived(object sender, TextMessageEventArgs e)
        {
            
            LogConversation((e.Sender!=null)?e.Sender.Mail:"", e.Message.Text, false);

            //if(!e.Sender.OnAllowedList) this.messenger.Nameserver.AddContactToList(e.Sender, MSNPSharp.MSNLists.AllowedList);
            MSNPSharp.SBMessageHandler sndr = (sender as MSNPSharp.SBMessageHandler);
            if (sndr != null)
            {
                System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(SleepyResponse), new object[] { e, sndr });
            }

        }
        private void SleepyResponse(object sender)
        {
            object[] data = (sender as object[]);
            TextMessageEventArgs e = (TextMessageEventArgs)data[0];
            MSNPSharp.SBMessageHandler sndr = (data[1] as MSNPSharp.SBMessageHandler);

            if (e != null && sndr != null)
            {
                string response = "";
                string[] message = e.Message.Text.Split(' ');
                string command = message[0].ToLower().Trim();
                string msg = e.Message.Text.Substring(command.Length).Trim();
                string contact = "";
                foreach (Contact listContact in sndr.Contacts.Keys)
                {
                    contact = listContact.Name;
                    break;
                }
                response = Plugins.PluginLoader.Chat(contact, command, msg);
                if (response == null) response = "";

                System.Diagnostics.Trace.WriteLine(string.Format("Contact:{0}, Command:{1}, Message:{2}, Response:{3}", contact, command, msg, response));

                LogConversation(e.Sender.Mail, response, true);
                //foreach (string responseMsg in response.Split('\n'))
                //{
                
                string r = response.Trim();
                if (r != "")
                {
                    string f = r.Replace("un-named user", e.Sender.NickName);
                    f = f.Replace("BOTNAME", Plugins.PluginLoader.ScreenName);
                    System.Threading.Thread.Sleep(e.Message.Text.Length * 100);

                    if (f.Length > 200)
                    {
                        foreach (string responseMsg in f.Split('\n'))
                        {
                            sndr.SendTypingMessage();
                            System.Threading.Thread.Sleep(responseMsg.Length * 50);
                            sndr.SendTextMessage(new MSNPSharp.TextMessage(responseMsg));
                        }
                    }
                    else
                    {
                        sndr.SendTypingMessage();
                        System.Threading.Thread.Sleep(f.Length * 100);
                        sndr.SendTextMessage(new MSNPSharp.TextMessage(f));
                    }
                }
                //}

            }
        }

		private void Nameserver_ReverseAdded(object sender, ContactEventArgs e) {

            if (!e.Contact.OnAllowedList)
            {
                this.messenger.Owner.NSMessageHandler.ContactService.AddNewContact(e.Contact.Mail);
                System.Diagnostics.Trace.WriteLine("New Contact Added:" + e.Contact.Mail);
            }
		}
	}
}
